package fmse.uaic.k.xml.adapter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import fmse.uaic.k.configuration_abstraction.abstract_classes.KAbstractNode;
import fmse.uaic.k.configuration_abstraction.data_structures.ConfigurationKNode;
import fmse.uaic.k.configuration_abstraction.data_structures.ConfigurationKNode.Multiplicity;
import fmse.uaic.k.configuration_abstraction.data_structures.ConfigurationKTree;
import fmse.uaic.k.configuration_abstraction.data_structures.RuleKNode;
import fmse.uaic.k.configuration_abstraction.data_structures.RuleKNode.Open;
import fmse.uaic.k.configuration_abstraction.data_structures.RuleKTree;

/**
 * This is only for testing ...
 * 
 * @author andreiarusoaie
 * 
 */
public class XmlToKAbstractTree {
	private DocumentBuilder parser;
//	private Queue<Node> bfs;

	public XmlToKAbstractTree() {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			parser = factory.newDocumentBuilder();

//			bfs = new LinkedList<Node>();

		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public ConfigurationKTree loadConfiguration(String file) {
		ConfigurationKNode root = null;
		try {
			Document document = parser.parse(new InputSource(file));

			// ConfigurationKNode node = null;

			if (document != null)
				root = recurseConfig(document.getDocumentElement());
			
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new ConfigurationKTree(root);
	}

	public RuleKTree loadRule(String file) {
		RuleKNode root = null;
		try {
			Document document = parser.parse(new InputSource(file));

			// ConfigurationKNode node = null;

			if (document != null)
				root = recurseRule(document.getDocumentElement());
			
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new RuleKTree(root);
	}

	
/*	private void bfs(Element node) {
		Queue<Node> queue = new LinkedList<Node>();
		queue.add(node);

		bfs_alg(queue);

		for (Node n : bfs) {
			System.out.print(n.getNodeName() + "( " + getChildNo(n) + " )  ");
		}
	}

	private void bfs_alg(Queue<Node> Q) {
		if (!Q.isEmpty()) {
			Node v = Q.remove();
			bfs.add(v);
			NodeList children = v.getChildNodes();
			for (int i = 0; i < children.getLength(); i++)
				if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
					Q.add(children.item(i));

			bfs_alg(Q);
		}
	}

	private int getChildNo(Node n) {
		int count = 0;
		NodeList children = n.getChildNodes();
		for (int i = 0; i < children.getLength(); i++)
			if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
				count++;
		return count;
	}

	private ConfigurationKNode buildConfigTree() {
		ConfigurationKNode knode = null, node;

		while (bfs.size() > 0) {
			Node xml = bfs.remove();

		}

		return knode;
	}
*/
	private ConfigurationKNode recurseConfig(Node node) {
		if (node != null) {
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				Node attr = node.getAttributes().getNamedItem("multiplicity");
				Multiplicity mul = Multiplicity.NONE;
				if (attr != null) {
					mul = getMultiplicity(attr.getNodeValue());
				}

				NodeList children = node.getChildNodes();
				if (children.getLength() == 0) {
					return createLeaf(node);
				} else {
					ConfigurationKNode thisnode = new ConfigurationKNode(
							node.hashCode(), node.getNodeName(), null, null,
							mul);
					List<KAbstractNode> chd = new ArrayList<KAbstractNode>();
					for (int i = 0; i < children.getLength(); i++) {
						Node child = children.item(i);
						ConfigurationKNode knode = recurseConfig(child);
						if (knode != null)
						{
							knode.setParent(thisnode);
							chd.add(knode);
						}
					}
					thisnode.setChildren(chd);
					return thisnode;
				}
			}
		}
		
		return null;
	}

	private RuleKNode recurseRule(Node node) {
		if (node != null) {
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				Node attr = node.getAttributes().getNamedItem("open");
				Open open = Open.NONE; 
				if (attr != null) {
					open = getOpen(attr.getNodeValue());
				}

				NodeList children = node.getChildNodes();
				if (children.getLength() == 0) {
					return createRLeaf(node);
				} else {
					RuleKNode thisnode = new RuleKNode(
							node.hashCode(), node.getNodeName(), null, null,
							open);
					List<KAbstractNode> chd = new ArrayList<KAbstractNode>();
					for (int i = 0; i < children.getLength(); i++) {
						Node child = children.item(i);
						RuleKNode knode = recurseRule(child);
						if (knode != null)
						{
							knode.setParent(thisnode);
							chd.add(knode);
						}
					}
					thisnode.setChildren(chd);
					return thisnode;
				}
			}
		}
		
		return null;
		
	}

	private RuleKNode createRLeaf(Node node) {
		Node attr = node.getAttributes().getNamedItem("open");
		Open open = Open.NONE; 
		if (attr != null) {
			open = getOpen(attr.getNodeValue());
		}
		
		return new RuleKNode(node.hashCode(), node.getNodeName(), null, null, open);
	}

	private ConfigurationKNode createLeaf(Node node) {
		Node attr = node.getAttributes().getNamedItem("open");
		Multiplicity mul = Multiplicity.NONE;
		if (attr != null) {
			mul = getMultiplicity(attr.getNodeValue());
		}
		return new ConfigurationKNode(node.hashCode(), node.getNodeName(),
				null, null, mul);
	}

	private Multiplicity getMultiplicity(String mul) {
		Multiplicity multiplicity = Multiplicity.NONE;

		if (mul.toLowerCase().equals("*") || mul.toLowerCase().equals("any")) {
			multiplicity = Multiplicity.ANY;
		} else if (mul.toLowerCase().equals("+")
				|| mul.toLowerCase().equals("some")) {
			multiplicity = Multiplicity.SOME;
		} else if (mul.toLowerCase().equals("?")
				|| mul.toLowerCase().equals("maybe")) {
			multiplicity = Multiplicity.MAYBE;
		}

		return multiplicity;
	}
	
	private Open getOpen(String mul) {
		Open open = Open.NONE;

		if (mul.toLowerCase().equals("left"))
			open = Open.LEFT;
		else if (mul.toLowerCase().equals("right"))
			open = Open.RIGHT;
		else if (mul.toLowerCase().equals("both"))
			open = Open.BOTH;

		return open;
	}
}
